package cn.itcast.service.impl;

import cn.itcast.constant.*;
import cn.itcast.context.BaseContext;
import cn.itcast.exception.*;
import cn.itcast.mapper.EmployeeMapper;
import cn.itcast.properties.JwtProperties;
import cn.itcast.result.PageBean;
import cn.itcast.service.EmployeeService;
import cn.itcast.utils.JwtUtil;
import cn.itheima.sys.dto.EmployeeDTO;
import cn.itheima.sys.dto.EmployeeLoginDTO;
import cn.itheima.sys.dto.EmployeePageQueryDTO;
import cn.itheima.sys.dto.PasswordEditDTO;
import cn.itheima.sys.pojo.Employee;
import cn.itheima.sys.vo.EmployeeLoginVO;
import cn.itheima.sys.vo.EmployeeVO;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@AllArgsConstructor
public class EmployeeServiceImpl implements EmployeeService {

    private EmployeeMapper employeeMapper;
    private JwtProperties jwtProperties;
    private RedisTemplate<String, Integer> redisTemplate;

    /**
     * 分页查询
     *
     * @param condition 分页及自定义条件
     * @return 员工分页列表
     */
    @Override
    public PageBean pageList(EmployeePageQueryDTO condition) {
        PageHelper.startPage(condition.getPage(), condition.getPageSize());
        Page<Employee> page = employeeMapper.list(condition);

        return new PageBean(page.getTotal(), page.getResult());
    }

    /**
     * 获取员工列表
     *
     * @return 所有员工
     */
    @Override
    public List<EmployeeVO> list() {
        ArrayList<EmployeeVO> employeeVOS = new ArrayList<>();
        BeanUtils.copyProperties(employeeMapper.list(null), employeeVOS);
        return employeeVOS;
    }

    /**
     * 新增员工
     *
     * @param employeeDTO 员工信息
     */
    @Override
    public void add(EmployeeDTO employeeDTO) {
        Employee byUsername = employeeMapper.getByUsername(employeeDTO.getUsername());
        if (byUsername != null) {
            throw new BaseException("账号已存在,添加失败!!!");
        }

        Employee employee = new Employee();
        //对象属性拷贝
        BeanUtils.copyProperties(employeeDTO, employee);

        //设置账号的状态，默认正常状态 1表示正常 0表示锁定
        employee.setStatus(StatusConstant.ENABLE);

        //设置默认密码
        employee.setPassword(BCrypt.hashpw(PasswordConstant.DEFAULT_PASSWORD, BCrypt.gensalt()));

        employeeMapper.add(employee);
    }

    /**
     * 修改员工
     *
     * @param employeeDTO 员工信息
     */
    @Override
    public void update(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();

        //对象属性拷贝
        BeanUtils.copyProperties(employeeDTO, employee);

        employeeMapper.update(employee);
    }

    /**
     * 根据id删除员工
     *
     * @param id 要删除的员工id
     */
    @Override
    public void deleteById(Long id) {
        employeeMapper.deleteById(id);
    }

    /**
     * 员工登录
     *
     * @param employeeLoginDTO 登录参数
     * @return 用户的jwt信息
     */
    @Override
    public EmployeeLoginVO login(EmployeeLoginDTO employeeLoginDTO) {
        Employee employee = employeeMapper.getByUsername(employeeLoginDTO.getUsername());

        String userKey = RedisConstant.USER_KEY + employeeLoginDTO.getUsername();
        String userLockedKey = RedisConstant.USER_LOCKED_KEY + employeeLoginDTO.getUsername();

        // 看是否被锁定
        Object o = redisTemplate.opsForValue().get(userLockedKey);
        if (o != null) {
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

        if (employee == null) {
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        if (Objects.equals(employee.getStatus(), StatusConstant.DISABLE)) { //判断账户是否被禁用
            throw new AccountDisabledException(MessageConstant.ACCOUNT_DISABLED);
        }

        //密码正确返回token
        if (BCrypt.checkpw(employeeLoginDTO.getPassword(), employee.getPassword())) {
            return getEmployeeLoginInfo(employee);
        } else {
            Integer count = redisTemplate.opsForValue().get(userKey);
            if (count == null) {
                //没有就往redis添加  并设置过期时间
                redisTemplate.opsForValue().set(userKey, 1, 1, TimeUnit.MINUTES);
            } else if (count < 3) { //小于3次直接加1写进去
                redisTemplate.opsForValue().set(userKey, count + 1, 0);
            } else {  //其他情况直接锁定账户
                redisTemplate.opsForValue().set(userLockedKey, 1, 1, TimeUnit.HOURS);
                throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
            }

            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }
    }

    /**
     * 启用、禁用员工账号
     *
     * @param status 状态
     * @param id     要操作的员工id
     */
    @Override
    public void updateStatus(Integer status, Long id) {
        Employee employee = Employee.builder()
                .status(status)
                .id(id)
                .build();

        employeeMapper.update(employee);
    }

    /**
     * 修改密码
     *
     * @param passwordEditDTO 修改密码
     */
    @Override
    public void editPassword(PasswordEditDTO passwordEditDTO) {
        Employee employeeById = employeeMapper.getById(BaseContext.getCurrentId());

        if (!BCrypt.checkpw(passwordEditDTO.getOldPassword(), employeeById.getPassword())) {
            throw new PasswordErrorException(String.format(MessageConstant.PASSWORD_EDIT_FAILED, "原始密码输入错误"));
        }

        employeeById.setPassword(BCrypt.hashpw(passwordEditDTO.getNewPassword(), BCrypt.gensalt()));

        employeeMapper.update(employeeById);
    }

    /**
     * 根据id查询员工
     *
     * @param id 要查询的员工id
     * @return 返回员工信息
     */
    @Override
    public EmployeeVO getById(Long id) {
        EmployeeVO employeeVO = new EmployeeVO();
        BeanUtils.copyProperties(employeeMapper.getById(id), employeeVO);

        return employeeVO;
    }

    /**
     * @param employee 登录信息
     * @return 登录返回信息
     */
    private EmployeeLoginVO getEmployeeLoginInfo(Employee employee) {

        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
        claims.put(JwtClaimsConstant.NAME, employee.getName());
        claims.put(JwtClaimsConstant.USERNAME, employee.getUsername());
        claims.put(JwtClaimsConstant.PHONE, employee.getPhone());

        String jwt = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), claims);

        return EmployeeLoginVO.builder()
                .id(employee.getId())
                .name(employee.getName())
                .userName(employee.getUsername())
                .token(jwt).build();
    }
}
